#ifndef __UTIL_H__
#define __UTIL_H__

#ifndef DEBUG
#define DEBUG 0
#endif

#include <iostream>
using std::cerr;
using std::endl;

#include <iomanip>
using std::ios;

#include <string>
using std::string;

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

typedef unsigned char uchar_t;
typedef unsigned int  uint_t;

template<class T>
struct matrix_t{
	int      X_SIZE;
	int      Y_SIZE;
	T** cells;
};

template<class T>
struct matrix_t_3D{
	int      X_SIZE;
	int      Y_SIZE;
	int      Z_SIZE;
	T*** cells;
};

void debug(const char * s, ...) {
	if (DEBUG > 0) {
		va_list args;
		va_start(args, s);
		vfprintf(stdout, s, args);
		fprintf(stdout, "\n");
		va_end(args);
	}
}

bool in_range(int val, int low, int high) {
	return low <= val && val <= high;
}

int random_int(int low, int high) {
	return (rand() % high) + low;
}

template<class T>
void matrix_allocate(matrix_t <T>* m, int rows, int cols) {
	m->X_SIZE  = cols;
	m->Y_SIZE  = rows;
	m->cells = new T*[rows];
	memset(m->cells,0,sizeof(T*) * rows);

	for(int i=0; i < rows; i++) {
		m->cells[i] = new T[cols];
		memset(m->cells[i],0,sizeof(T) * cols);
	}
}

template<class T>
void matrix_allocate_3D(matrix_t_3D <T>* m, int x, int y, int z) {
	m->X_SIZE  = x;
	m->Y_SIZE  = y;
	m->Z_SIZE  = z;
	m->cells = new T**[x];
	memset(m->cells,0,sizeof(T**) * x);

	for(int i=0; i < x; i++) {
		m->cells[i] = new T*[y];
		memset(m->cells[i],0,sizeof(T*) * y);
		
		for(int j=0; j < y; j++) {
			m->cells[i][j] = new T[z];
			memset(m->cells[i][j],0,sizeof(T) * z);
		}
	}
	
}

template<class T>
void matrix_teardown(matrix_t <T> &m) {
	if (m.cells == NULL)
		return;

	for(int i=0; i < m.X_SIZE; i++) {
		if (m.cells[i] != NULL)
			delete [] m.cells[i];
	}
	delete [] m.cells;
	m.cells = NULL;
	m.X_SIZE  = 0;
	m.Y_SIZE  = 0;
}

template<class T>
void matrix_teardown_3D(matrix_t_3D <T> &m) {
	if (m.cells == NULL)
		return;

	for(int i=0; i < m.X_SIZE; i++)
	{
		for(int j=0; j < m.Y_SIZE; j++)
		{
			if (m.cells[i][j] != NULL)
				delete [] m.cells[i][j];
		}
		
		if (m.cells[i] != NULL)
			delete [] m.cells[i];
	}
	delete [] m.cells;
	m.cells = NULL;
	m.X_SIZE  = 0;
	m.Y_SIZE  = 0;
	m.Z_SIZE  = 0;
}

template<class T>
bool matrix_transpose(matrix_t <T>* a, matrix_t <T>* at) {
	if (a->X_SIZE != at->Y_SIZE)
		return false;

	if (a->Y_SIZE != at->X_SIZE)
		return false;

	for(int i=0; i < a->Y_SIZE; i++) {
		for(int j=0; j < a->X_SIZE; j++) {
			at->cells[j][i] = a->cells[i][j];
		}
	}

	return true;
}

template<class T>
bool matrix_multiply(matrix_t <T>* a, matrix_t <T>* b, matrix_t <T>* c) {
	double sum = 0.0;

	if (a->X_SIZE != b->Y_SIZE)
		return false;

	//matrix_allocate(c,a->rows,b->cols);

	for (int i=0; i < a->Y_SIZE; i++) {
		for (int j=0; j < b->X_SIZE; j++) {
			c->cells[i][j] = 0.0;
			for (int m=0; m < a->X_SIZE; m++) {
				c->cells[i][j] += a->cells[i][m] * b->cells[m][j];
			}
		}
	}

	return true;
}

template <class T>
void find_max_2d(matrix_t <T>* m, int &max_i, int &max_j) {
	int i,j;

	max_i = 0;
	max_j = 0;
	for (i=0; i < m->Y_SIZE; i++) {
		for (j=0; j < m->X_SIZE; j++) {
			if (m->cells[max_i][max_j] < m->cells[i][j]) {
				max_i = i;
				max_j = j;
			}
		}
	}
}

template <class T>
void find_max_3d(matrix_t_3D <T>* m, int &max_i, int &max_j, int &max_k) {
	int i,j, k;

	max_i = 0;
	max_j = 0;
	max_k = 0;
	for (i=0; i < m->Y_SIZE; i++) {
		for (j=0; j < m->X_SIZE; j++) {
			for (k=0; k < m->Z_SIZE; k++) {
				if (m->cells[max_i][max_j][max_k].votes < m->cells[i][j][k].votes) {
					max_i = i;
					max_j = j;
					max_k = k;
				}
			}
		}
	}
}

template<class T>
void matrix_print(const string s, const matrix_t <T>* a) {
	cout << s << " ("<< a->Y_SIZE << "x" << a->X_SIZE << ")" << endl;
	for (int i=0; i < a->Y_SIZE; i++) {
		for (int j=0; j < a->X_SIZE; j++) {
			cout.width(8);
			cout.fill(' ');
			if (a->cells[i][j] < 10.0)
				cout.precision(5);
			else if (a->cells[i][j] < 100.0)
				cout.precision(4);
			else if (a->cells[i][j] < 1000.0)
				cout.precision(3);
			else
				cout.precision(2);
			cout.flags(ios::right | ios::fixed);
			cout << a->cells[i][j];
			if ((j+1) != a->X_SIZE)
				cout << " ";
		}
		cout << endl;
	}
}

bool handleError(string message) {
	cerr << message << endl;
	return false;
}

float compute_secs(const timespec *t) {
	return t->tv_sec + (t->tv_nsec / 1000000000.0);
}

void print_time(const timespec *t, const char *s) {
	float computed_time = compute_secs(t);
	printf("%s: %f sec\n", s, computed_time);
}

typedef struct {
	timespec start;
	timespec end;
	timespec elapsed;
} hr_timer_t;

void time_diff(timespec* start, timespec* end, timespec* diff) {
	if ((end->tv_nsec - start->tv_nsec) < 0) {
		diff->tv_sec  = end->tv_sec - start->tv_sec - 1;
		diff->tv_nsec = 1000000000 + end->tv_nsec - start->tv_nsec;
	} else {
		diff->tv_sec  = end->tv_sec - start->tv_sec;
		diff->tv_nsec = end->tv_nsec - start->tv_nsec;
	}
}


bool timer_start(hr_timer_t* timer) {
//	clockid_t cid;

	clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &timer->start);

/*	if (clock_getcpuclockid(0,&cid) == ENOENT) {
		debug("Process switched cpu which invalidated timer!");
		return false;
	}
*/
	return true;
}

bool timer_end(hr_timer_t* timer) {
//	clockid_t cid;

	clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &timer->end);

/*	if (clock_getcpuclockid(0,&cid) == ENOENT) {
		debug("Process switched cpu which invalidated timer!");
		return false;
	}
*/
	time_diff(&timer->start,&timer->end,&timer->elapsed);

	return true;
}



#endif
